package com.shiro.sdk.config;

import com.shiro.sdk.filter.JwtAuthorizationFilter;
import com.shiro.sdk.properties.JwtProperties;
import com.shiro.sdk.properties.ShiroProperties;
import com.shiro.sdk.realm.JwtAuthorizingRealm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Configuration
@EnableConfigurationProperties({JwtProperties.class, ShiroProperties.class})
@ConditionalOnProperty(prefix = "shiro", value = "enable", havingValue = "true", matchIfMissing = true)
public class ShiroJwtAutoConfig implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Resource
    private ShiroProperties shiroProperties;

    @Resource
    private JwtProperties jwtProperties;

    /*private JwtAuthorizingRealm jwtAuthorizingRealm;

    @Bean
    @ConditionalOnMissingBean
    public JwtAuthorizingRealm jwtAuthorizingRealm() {
        log.debug("Shiro Bean JwtAuthorizingRealm 初始化 ...");

        Map<String, JwtAuthorizingRealm> beans = applicationContext.getBeansOfType(JwtAuthorizingRealm.class);

        if (beans == null || beans.size() == 0) {
           throw new RuntimeException("请继承 JwtAuthorizingRealm 并实现它的方法");
        }
        for(JwtAuthorizingRealm jwtAuthorizingRealm : beans.values()){
            jwtAuthorizingRealm = jwtAuthorizingRealm;
            break;
        }

        jwtAuthorizingRealm.setCredentialsMatcher(new JwtCredentialsMatcher(jwtProperties));

        if (shiroProperties.isEnableCacheMemory()) {

            // 使用默认提供的 MemoryConstrainedCacheManager
            jwtAuthorizingRealm.setCacheManager(new MemoryConstrainedCacheManager());

            // 启用身份验证缓存，即缓存AuthenticationInfo信息，默认false
            jwtAuthorizingRealm.setAuthenticationCachingEnabled(true);

            // 启用授权缓存，即缓存AuthorizationInfo信息，默认false,一旦配置了缓存管理器，授权缓存默认开启
            jwtAuthorizingRealm.setAuthorizationCachingEnabled(true);
        }

        return jwtAuthorizingRealm;
    }*/

    @Bean
    @ConditionalOnMissingBean
    public DefaultWebSecurityManager securityManager() {
        log.info("Shiro Bean SecurityManager 初始化 ...");

        // 交由DefaultWebSecurityManager管理
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();

        Map<String, JwtAuthorizingRealm> beans = applicationContext.getBeansOfType(JwtAuthorizingRealm.class);

        manager.setRealms(new ArrayList<>(beans.values()));

        /*
         * 使用JWT方式进行访问控制，因此关闭 shiro 自带的 session管理，详情见文档
         * http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
         */
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        manager.setSubjectDAO(subjectDAO);

        // 禁用remberMe功能
        manager.setRememberMeManager(null);

        return manager;
    }

    @Bean
    @ConditionalOnMissingBean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        log.info("Shiro Bean ShiroFilterFactoryBean 初始化 ...");

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl());

        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl(shiroProperties.getIndexUrl());

        // 未授权跳转页面
        shiroFilterFactoryBean.setUnauthorizedUrl(shiroProperties.getUnauthorizedUrl());

        //配置需要忽略的路径
        LinkedHashMap<String, String> ignoresUrlMap = getIgnoresUrlMap(shiroProperties.getIgnoresUrlList());
        shiroFilterFactoryBean.setFilterChainDefinitionMap(ignoresUrlMap);

        //配置相关过滤器
        LinkedHashMap<String, Filter> filterMap = new LinkedHashMap<>(3);
        filterMap.put("jwtAuthorizationFilter", new JwtAuthorizationFilter(shiroProperties));
        /*filterMap.put("jwtPermissionFilter", new JwtPermissionFilter());
        filterMap.put("jwtRoleFilter", new JwtRoleFilter());*/
        shiroFilterFactoryBean.setFilters(filterMap);
        Map<String, String> filterRuleMap = new HashMap<>();


        // 所有请求通过我们自己的JWT Filter
        filterRuleMap.put("/**", "jwtAuthorizationFilter");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return shiroFilterFactoryBean;
    }

    private LinkedHashMap<String, String> getIgnoresUrlMap(String ignoresUrlList) {
        if(StringUtils.isBlank(ignoresUrlList)){
            return new LinkedHashMap();
        }
        String[] urls = StringUtils.split(ignoresUrlList, ",");
        if(urls == null || urls.length <= 0){
           return new LinkedHashMap();
        }

        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(urls.length);
        Arrays.stream(urls).map(url -> { map.put(url, "anon");return map; }).collect(Collectors.toList());
        return map;
    }

    /**
     * 开启shiro注解支持，例如 @RequiresPermissions
     * @param securityManager DefaultWebSecurityManager
     * @return AuthorizationAttributeSourceAdvisor
     */
    @Bean
    @ConditionalOnMissingBean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
